/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package Household;
import MortgageCore.*;
import Bank.Bank;
import java.util.*;
import simframe.simulation.*;
/**
 *
 * @author karakots
 */
public class HomeBehavior {
     private MortgageMarket mortgageMarket;
     private HouseMarket houseMarket;
     private Household myHH;
     private House houseToBuy;
     private House ourHouse;
     private double rent;
     private int monthsToNextRent;
     private boolean getInitialMortgage;
     private double initialDownPaymentBuffer;
     private boolean tryingToMove;
     private int movedInTheLastMonth;
     private boolean tryingToRefinance;
     private int refinancedInLastMonth;
     private int missedPaymentLastMonth;
     private boolean remodeling;
     private double downPaymentResources;
     private double myEstimatePctDown;
     private boolean offerAccepted;

     // home buying
     private MortgageOffer myMortgageBid;
     private Mortgage fundedMortgage;
     private Mortgage preFundedMortgage;
     private int missedMortgagePayment;
     private int daysToFirstMortgagePayment;
     private int numMortgageApps;
     private boolean isHomeowner;

     // variables HH uses to evaulate a mortgage
     private int minLength;
     private int maxLength;
     private double minLengthUtil;
     private double maxLengthUtil;
     private double minInterestRate;
     private double maxInterestRate;
     private double minInterestRateUtil;
     private double maxInterestRateUtil;
     private double minpoints = 0;
     private double maxPoints = 0.01;
     private double minPointsUtil;
     private double maxPointsUtil;
     private double minFixedRate = 0;        // variable rate
     private double maxFixedRate = 1;        // fixed Rate
     private double minFixedRateUtil;
     private double maxFixedRateUtil;
     private boolean debug_paidOffMortgage;
     private boolean debug_preAppMortgageExpired;
     private int waitingToMoveCounter;

     public List<String> mortgageRecord;
     private boolean logData;
     private boolean enableLogData;
     private static final double downPaymentEstFactor = 0.5;


     //
     // accessors
     //
     public void setLogData(boolean b) {
         if (enableLogData) {
             logData = b;
             if (logData && (mortgageRecord == null)) {
                 mortgageRecord = new ArrayList<String>();
             }
         }
     }
     public boolean getLogData()                        {return logData;}
     public void setEnableLogData(boolean b)            {enableLogData = b;}
     public boolean getEnableLogData()                  {return enableLogData;}
     public boolean getIsHomeowner()                    {return isHomeowner;}
     public double getRent()                            {return rent;}
     public void setMortgageMarket(MortgageMarket m)    {mortgageMarket = m;}
     public boolean hasActiveMortgageRequest()          {return (myMortgageBid != null);}
     public double getMinPointsUtil()                   {return minPointsUtil;}
     public double getMaxPointsUtil()                   {return maxPointsUtil;}
     public double getMinFixedRateUtil ()               {return minFixedRateUtil;}
     public double getMaxFixedRateUtil()                {return maxFixedRateUtil;}
     public double getMinLengthUtil()                   {return minLengthUtil;}
     public double getMaxLengthUtil()                   {return maxLengthUtil;}
     public int getMinLength()                          {return minLength;}
     public int getMaxLength()                          {return maxLength;}
     public boolean getHasPreApprovedLoan()             {return preFundedMortgage != null;}
     public Mortgage getMortgage()                      {return fundedMortgage;}
     public int getNumMissedMortgagePayments()          {return missedMortgagePayment;}
     public int getNumMortgageApps()                    {return numMortgageApps;}
     public void setHouseMarket(HouseMarket hm)         {houseMarket = hm;}
     public boolean getTryingToMove()                   {return tryingToMove;}
     public boolean getMovedInLastMonth()               {return (movedInTheLastMonth > 0);}
     public boolean getRefinancedInLastMonth()          {return (refinancedInLastMonth > 0);}
     public boolean getTryingToRefinance()              {return tryingToRefinance;}
     public boolean getMissedPaymentLastMonth()         {return (missedPaymentLastMonth > 0);}
     public double getInitialDownPaymentBuffer()        {return initialDownPaymentBuffer;}
     public boolean hasHouse()                          {return ourHouse != null;}
     public boolean getInDefault() {
         if (fundedMortgage == null)
             return false;
         else
            return (fundedMortgage.getNumMissedPayments() != 0);
     }
     public double getHomePurchasePrice() {
         if (this.ourHouse != null)
             return ourHouse.getPurchasePrice();
         else
             return 0;
     }
     public double getHomeQ() {
         if (isHomeowner) {
             if (ourHouse != null)
                return ourHouse.getHouseQ();
             else
                 return 0;
         }
         else
             return 0;
     }

     private String getDate() {
         long day = myHH.getTime() / simframe.simulation.Time.Day;
         int year = 1991 + (int) day/360;
         int dayOfYear = (int) day % 360;
         int month = (dayOfYear / 30) + 1;
         String monthName;
         switch (month) {
             case 1:        monthName = "Jan ";  break;
             case 2:        monthName = "Feb ";  break;
             case 3:        monthName = "Mar ";  break;
             case 4:        monthName = "Apr ";  break;
             case 5:        monthName = "May ";  break;
             case 6:        monthName = "Jun ";  break;
             case 7:        monthName = "Jul ";  break;
             case 8:        monthName = "Aug ";  break;
             case 9:        monthName = "Sep ";  break;
             case 10:       monthName = "Oct ";  break;
             case 11:       monthName = "Nov ";  break;
             case 12:       monthName = "Dec ";  break;
             default:       monthName = "??";   break;
         }
         int dayOfMonth = dayOfYear - ((month-1) * 30) + 1;
         String date = new String(monthName + dayOfMonth + ", " + year + ": ");
         return date;
     }

    HomeBehavior(Household hh) {
        myHH= hh;
        rent = 0;
        mortgageMarket = null;
        houseToBuy = null;
        ourHouse = null;
        numMortgageApps = 0;
        isHomeowner = false;
        monthsToNextRent = (int) (12 * Math.random());
        myEstimatePctDown = downPaymentEstFactor * Math.random();
        getInitialMortgage = true;
        logData = false;

        // home buying
        myMortgageBid = null;
        missedMortgagePayment = 0;
        daysToFirstMortgagePayment = 0;

        minLength = 15;
        maxLength = 30;
        minLengthUtil = 1.0 * Math.random();
        maxLengthUtil = 1.0 * Math.random();

        minInterestRate = 0;
        maxInterestRate = 0.1;
        minInterestRateUtil = 10.0 * Math.random();
        maxInterestRateUtil = 0;

        minpoints = 0;
        maxPoints = 0.1 * Math.random();
        minPointsUtil = 1.0 * Math.random();
        maxPointsUtil = 0;

        minFixedRate = 0;        // variable rate
        maxFixedRate = 1;        // fixed Rate
        minFixedRateUtil = 2.0 * Math.random();
        maxFixedRateUtil = 2.0 * Math.random();

    }

    public void rentTimer() {
        if (monthsToNextRent > 0) {
            monthsToNextRent -= 1;
        }
    }

    private void becomeUnstuck() {
        if (tryingToMove) {
            waitingToMoveCounter++;
            if (waitingToMoveCounter > 100) {
                if (preFundedMortgage == null) {
                    tryingToMove = false;
                    myMortgageBid = null;
                }
            }
        } else
            waitingToMoveCounter = 0;
    }

    // month in the life of household
    public void considerHousingAction() {
        if (daysToFirstMortgagePayment > 0)
            daysToFirstMortgagePayment--;
        if (movedInTheLastMonth > 0)
            movedInTheLastMonth--;
        if (refinancedInLastMonth > 0)
            refinancedInLastMonth--;
        if (missedPaymentLastMonth > 0)
            missedPaymentLastMonth--;

        becomeUnstuck();

        if (myHH.getDayOfMonth() != myHH.getMyDayOfMonth())
            return;

        // make mortgage payment separate from the following,
        // because hh may be tryign to get another nortgage while paying the current one,
        // either for a new home purchase or a refinance of the existing home
        if ((fundedMortgage != null) && (daysToFirstMortgagePayment == 0)) {
            double payment = fundedMortgage.getMonthlyPayment();
            tryToPayMortgage(payment);
        }

        if (getInitialMortgage) {
            getInitialStateMortgage();
        } else if ((preFundedMortgage == null) && (tryingToMove == false) && (houseToBuy == null)) {
            if (!isHomeowner)
                // consider buyign a first home
                renterMaybeApplyForMortgage();
            else {
                maybeLowerMortgagePayments();
                considerSellBuyHome();
                maybeRemodel();
            }

        }
    }

    private void getInitialStateMortgage() {
        // when we start up the sim, we want to assign homes to households
        houseToBuy = houseMarket.getHouseToBuy(myHH.getResidenceState(), myHH.getIncomePctAverage());
        houseToBuy.setInitializationAttempt(true);
        double amountToBorrow = houseToBuy.getPurchasePrice();
        // get enough cash locally to pay downpayment
        initialDownPaymentBuffer = amountToBorrow * downPaymentEstFactor * Math.random();
        generateMortgageRequest(amountToBorrow);
        getInitialMortgage = false;
    }

    private void maybeLowerMortgagePayments() {
        if(tryingToMove || tryingToRefinance || remodeling || (fundedMortgage == null))
            return;

        // probability of refi attempt once per three months on average
        if (Math.random() > 0.33)
            return;

        // see if it is even feasible
        // how are rates?
        Bank bestRateBank = mortgageMarket.getBestRateBank();
        double bestRate = bestRateBank.getLendingPolicy().monthlyMortgageInterestRate;

        double amountOwed = fundedMortgage.getDebtOwed();
        double points = bestRateBank.getLendingPolicy().mortgagePoints;
        if (bestRate < fundedMortgage.getTerms().getMonthlyInterestRate()) {
            // is it worth my while to lower my payments?
            double i = fundedMortgage.getTerms().getMonthlyInterestRate();
            double n = fundedMortgage.getTerms().getLengthInYears() * 12;
            double newPayment = bestRateBank.getMonthlyPayment(i, n, amountOwed);
            double currentPayment = fundedMortgage.getMonthlyPayment();
            double monthlySavings = currentPayment - newPayment;
            // we ought to consider "unamoritized points from previous loan
            // assume five year horizon
            if (monthlySavings <  (points / 60))
               return;
            // we want to lower payments by at least 10% to make it worthwhile
            if (monthlySavings/currentPayment < 0.1)
                return;
        } else  // rate not better
            return;

        // do I have enough to pay points?
        double refiAssets = getHomeEquity() + myHH.getTotalCashAssets();
        double pointsToPay = amountOwed * points;
        if (refiAssets < pointsToPay)
            return;

        // do I meet the min down payment?
        double currentHomeValue = getCurrentHomeValue();
        double downPaymentPctAvailable = (refiAssets - pointsToPay) / currentHomeValue;
        if (downPaymentPctAvailable < bestRateBank.getLendingPolicy().minMortgagePctDown)
            return;

        // I can make the paynments and have enough equity
        tryingToRefinance = true;
        // note that we might not get the terms we just estimatd with!
        houseToBuy = ourHouse;
        generateMortgageRequest(amountOwed);
    }

    private void maybeRemodel() {
        if(tryingToMove || tryingToRefinance || remodeling)
            return;
    }

    void considerSellBuyHome() {
        if (tryingToMove || tryingToRefinance || remodeling)
            return;
        // consider once ever 5 years (60 months) with a 50% chance of decising to move
        // 1/120 = 0.0083
        double randVal = Math.random();
        if (randVal > 0.0083)
            return;

        // get current value of home
        double equity = getHomeEquity();

        // get average age of adults in HH
        int numPeople = myHH.getMyInfo().people.size();
        int avgAge = 0;
        int numPeeps = 0;
        for (int i = 0; i < numPeople; i++) {
            Person peep = myHH.getMyInfo().people.get(i);
            if (peep.age > 20) {
                numPeeps++;
                avgAge += peep.age;
            }
        }
        if (numPeeps == 0)
            return;
        else
            avgAge /= numPeeps;

        // choose desired home
        double newHomeQ = ourHouse.getHouseQ();
        // if younger, upsize
        if (avgAge < 40)
            newHomeQ *= (1 + (Math.random()*0.5));
        // if older, downsize
        else if (avgAge > 65)
            newHomeQ *= (1 - (Math.random()*0.2));
        // otherwise, homeQ stays the same
        // fuzz it up, with an upward bias
        if (Math.random() < 0.5)
            newHomeQ *= 1 + (Math.random()*0.2);
        else
            newHomeQ *= 1 - (Math.random()*0.1);

        // scale up newHomeQ
        //newHomeQ *= 1.3;

        // see if we have enough to buy the next home
        double desiredHomePrice = houseMarket.getCurrentValue(myHH.getResidenceState(), newHomeQ);
        houseToBuy = new House();
        houseToBuy.setHouseQ(newHomeQ);
        houseToBuy.setPurchasePrice(desiredHomePrice);
        double transactionCost = houseToBuy.getTransactionCost();
        downPaymentResources = equity + myHH.getTotalCashAssets() - transactionCost;
        // if we don't have anythign for a downpayment, forget it!
        double pctDownAvailable = downPaymentResources / desiredHomePrice;
        // if we're low onthe downpayment
        if (pctDownAvailable < myEstimatePctDown) {
            // we don't yet know what percent down we can get, so we'll estimate
            // if we don;t have that much, then cancel the transaction
            houseToBuy = null;  // never mind!
            return;
        }
        // try to get a mortgage
        // all ready to go-- need to handle geting a mortgage when hoemowner already has a house!
        tryingToMove = true;
        if (downPaymentResources > desiredHomePrice) {
            // we can buy the new home with cash
            doTransaction();
        } else
        generateMortgageRequest(desiredHomePrice - downPaymentResources);   // this will set our state to REQUEST_OUT
    }

    public double getHomeEquity() {
        if (ourHouse == null)
            return 0;

        double ourHouseCurrValue = getCurrentHomeValue();
        double equity = ourHouseCurrValue;
        if (fundedMortgage != null)
            equity -= fundedMortgage.getDebtOwed();
        return equity;
    }

    private double getCurrentHomeValue() {
        return houseMarket.getCurrentValue(myHH.getResidenceState(), ourHouse.getHouseQ());
    }

    private void renterMaybeApplyForMortgage() {
        // 1/60 chance per month of deciding to becoime a homeowner
        // and, if we already decided to be a homeowner but our request expired,
        // we generate another request without waiting
        // If we had been preapproved and preapproved fundedMortgage expired, we need to wait the random time again
        // if (requestExpired || (Math.random() < 0.017)) {
        if (Math.random() < 0.017) {
            houseToBuy = houseMarket.getHouseToBuy(myHH.getResidenceState(), myHH.getIncomePctMedian());
            double downPaymentPctEstimate = downPaymentEstFactor * Math.random();
            double amountToBorrow = houseToBuy.getPurchasePrice() * (1 - downPaymentPctEstimate);
            if (myHH.getTotalCashAssets() > (amountToBorrow * downPaymentPctEstimate)) {                generateMortgageRequest(amountToBorrow);
            }
            else
               houseToBuy = null;
        }
    }

    private void generateMortgageRequest(double amountToBorow) {
        // generate a offer to buy a fundedMortgage
        // we just have to make it active and add it to the market
        // the market will use our utiity function to evaluate any offers

        // if we get here and already have a bid out, then don;t create another
        if (myMortgageBid != null)
            return;

        myMortgageBid = new MortgageOffer();
        myMortgageBid.setAgent(myHH);
        myMortgageBid.setIsActiveOffer(true);
        myMortgageBid.setType(MortgageOffer.Type.Bid);
        myMortgageBid.setAmountToBorrow(amountToBorow);
        if (getInitialMortgage)
            myMortgageBid.setDaysOfferValid(360);
        else
            myMortgageBid.setDaysOfferValid(90);
        mortgageMarket.addOffer(myMortgageBid);
        numMortgageApps++;
    }

     public synchronized void offerAccepted(MortgageOffer offer) {
        // means that some bank has accepted my offer--
        // bank will get offer accepted message, but bank does nothing with it

        offerAccepted = true;

        // we can't get rid of the offer yet, because the mortgage market needs it
        // to trigger the mortgage preapproval
        // we don't need the bid anymore

        // the next thing that should happen is that I'll get a preapproved mortgage;
    }

    public synchronized void offerExpired(MortgageOffer offer) {
        // this happens if we didn't qualify for a mortgage
        declineMortgageAndCancelPurchase();
        // if we had an initial house, we need to get rid of it
        // because we could not afford to buy one
        if ((houseToBuy != null) && (houseToBuy.getInitializationAttempt()))
            initialDownPaymentBuffer = 0;   // we'll need to save up!
    }

     public synchronized void mortgagePreapproval(Mortgage m) {
         // I can now free up the bid, as it has served its final purpose of
         // triggering the preapproval from the bank
         myMortgageBid = null;

         // if I got here without an accepted offer, should not take the mortgage
         if (!offerAccepted)
             return;

         // if I have not found a house to buy,should not take the mortgage
         // I find the house first so that OI know how much to borrow.
         // In the future, we may want to change this and get preapproval before we have ahouse
         if (houseToBuy == null)
             return;

         // if we somehow already got a pre-approved mortgage, we should not get another one
         if (preFundedMortgage != null)
             return;

         // if we are refinancing, make sure its a better deal
         if (tryingToRefinance) {
            double newRate = m.getTerms().getMonthlyInterestRate();
            double currentRate = fundedMortgage.getTerms().getMonthlyInterestRate();
            // check for interest rate at least 5% better
            if (currentRate <= (newRate * 0.95))
                declineMortgageAndCancelPurchase();
         }

         // if we are still here, accept the mortgage
         addMortgage(m);
         debug_preAppMortgageExpired = false;
    }

    public synchronized void preAppMortgageExpired(Mortgage m) {
        // this ocurs if we could not buy the house we wanted or sell a current house
        preFundedMortgage = null;
        tryingToMove = false;
        houseToBuy = null;
        offerAccepted = false;
        myMortgageBid = null;
        debug_preAppMortgageExpired = true;
    }


     private void addMortgage(Mortgage m) {
         preFundedMortgage = m;
         // if initial house purchase, buy house now
         maybeBuyHouse();
    }

    private void maybeBuyHouse() {
        boolean requestFunding = false;
        if (houseToBuy == null) {
            // we should never get here, because we don't request a mortgage unless we have a house to buy
            declineMortgageAndCancelPurchase();
        } else if (houseToBuy.getInitializationAttempt()) {
            // set the fundedMortgage to 20% of the prospective house
            double amountToBorrow = houseToBuy.getPurchasePrice() * (1 - preFundedMortgage.getTerms().getMinDownPayment());
            preFundedMortgage.setAmountToBorrow(amountToBorrow);
            requestFunding = true;
        } else if (tryingToMove) {
            double mortagePctDown = preFundedMortgage.getTerms().getMinDownPayment();
            if (mortagePctDown > myEstimatePctDown) {
                // we did not estimate enough of a down payment, so we can't do the deal
                // we need to save up more and try again later
                myEstimatePctDown = mortagePctDown;
                declineMortgageAndCancelPurchase();
            } else {
                double amountToBorrow = houseToBuy.getPurchasePrice() - downPaymentResources;
                preFundedMortgage.setAmountToBorrow(amountToBorrow);
                requestFunding = true;
            }
        } else if (tryingToRefinance) {
            double amountToBorrow = fundedMortgage.getDebtOwed();
            preFundedMortgage.setAmountToBorrow(amountToBorrow);
            requestFunding = true;
        } else if (Math.random() < 0.5) {
            // 50% chance to buy first home
            double amountToBorrow = preFundedMortgage.getPreapprovedAmount();
            preFundedMortgage.setAmountToBorrow(amountToBorrow);
            requestFunding = true;
        }

        if (requestFunding) {
             if (preFundedMortgage.getAmountToBorrow() > preFundedMortgage.getPreapprovedAmount()) {
                declineMortgageAndCancelPurchase();
            }
            else {
              if (this.debug_preAppMortgageExpired)
                  declineMortgageAndCancelPurchase();
              else
                // first stop preapproved mortgage from expiring!
                preFundedMortgage.becomeFunded();
            }
        }
    }

    private void declineMortgageAndCancelPurchase() {
         // tell the bank we're declining the fundedMortgage, so that
         // the bank can stop aging the Mortgage-- we don't want to get an expired message
        // when we're in the middle of another buying cycle!
         if (preFundedMortgage != null) {
             preFundedMortgage.borrowerCancel();
        }
        preFundedMortgage = null;
        tryingToMove = false;
        houseToBuy = null;
        offerAccepted = false;
        myMortgageBid = null;
    }

     public void mortgageFundedMessage(Mortgage m) {
         daysToFirstMortgagePayment = 30;
        if ((preFundedMortgage == null) || debug_preAppMortgageExpired) {
            // we dould end up here if the offer expired simultaneous with getting funded
            // inthat case we should abort whatever we were tryng to do
            declineMortgageAndCancelPurchase();
            return;
        }

         doTransaction();
         debug_paidOffMortgage = false;
     }

     private void doTransaction() {
         if (debug_preAppMortgageExpired) {
             declineMortgageAndCancelPurchase();
             return;
         }
 
         // we have the cash!
        if (tryingToRefinance) {
            refinance();
        } else {
            // may need to sell existing house first
            if (ourHouse != null) { // ned to dd logic to handle can in which we can't sell the house
                sellOurHouse();
            }
            // once we sell any otherhouse, we no longer have a funded mortgage
            // so now we can move the prefunded mortgage to the funded mortgage
            // we'll do this inside buyHouse
            buyHouse();
        }
    }

    private void refinance() {
         myHH.addCash(preFundedMortgage.getFundedAmount());
         double payoff = fundedMortgage.getDebtOwed();
         payMortgage(payoff);
         fundedMortgage = preFundedMortgage;
         preFundedMortgage = null;    // free up the old fundedMortgage
         // pay the points
         double points = fundedMortgage.getTerms().getPoints() * houseToBuy.getPurchasePrice();
         myHH.raiseCash(points);
         fundedMortgage.payPoints(points);
         refinancedInLastMonth = 30;
         //houseToBuy = ourHouse;
         // good to go!

         if (logData) {
            int paymentInt = (int) fundedMortgage.getMonthlyPayment();
            int refiInt = (int) fundedMortgage.getDebtOwed();
            int pointsInt = (int) points;
            String intRate = String.format("%2.2f",(fundedMortgage.getTerms().getMonthlyInterestRate() * 1200));
            mortgageRecord.add(getDate() + " Refi. $" + refiInt + " @ " + intRate + "% Payment= $" + paymentInt +
                 " points= $" + pointsInt + " from " + fundedMortgage.getBank().getName());
         }
     }

    private void payMortgage(double amount) {
        fundedMortgage.makePayment(amount);
        myHH.payCash(amount);
    }

    private void sellOurHouse() {
        // tell the house market, though it will do nothing at the moment
        houseMarket.homeSold();

        // turn the proceeds of the sale into cash locally
        double homePrice = getCurrentHomeValue();
        myHH.addCash(homePrice);

        // relinquish the house
        ourHouse = null;
        if (fundedMortgage == null) {
            // do nothing.  For whatever reason we have a house but no mortgage
        } else {
            // pay off the current fundedMortgage, if there is one
            double payOffAmount = fundedMortgage.getDebtOwed();
            if (!myHH.raiseCash(payOffAmount)) {
                // if we get here, we can't afford to do the deal!
                boolean wawaoo = true;
            }
            payMortgage(payOffAmount);
            // the above will trigger
            //  fundedMortgage -> bank paidInFull(), a synchromized method
            //  fundedMortgage -> hh mortgagePaidInFull, a synchronized method
            //  which releases the curent fundedMortgage
            // which frees us to replae it with the new fundedMortgage when we buy the new house
        }
        
        if (logData) {
            int priceInt = (int) homePrice;
            mortgageRecord.add(getDate() + " Sold home = $" + priceInt);
        }

    }

     public synchronized void mortgagePaidInFull(Mortgage m) {
         fundedMortgage = null;
         debug_paidOffMortgage = true;
     }

     private void buyHouse() {
         /*
         if (logData) {
            long day = myHH.getTime() / simframe.simulation.Time.Day;
            mortgageRecord.add("Day " + day + " About to buy a home");
         }
         */
         if (fundedMortgage == null) {
             fundedMortgage = preFundedMortgage;
             preFundedMortgage = null;    // free up the old fundedMortgage
         } else {
             // we should not end up here with a funded mortgaage, because that would mean we did not pay it off!
             boolean wawaoo = true;
         }
         // only need to do the following if there is a fundd mortgage
         double downPayment;
         double points;
         double transactionCost;
         double homePrice = houseToBuy.getPurchasePrice();
         if (fundedMortgage != null) {
            // compute down payment
            if (downPaymentResources == 0)
                 downPayment = fundedMortgage.getTerms().getMinDownPayment() * homePrice;
            else
                downPayment = downPaymentResources;
            points = fundedMortgage.getTerms().getPoints() * homePrice;
         } else {
             downPayment = homePrice;
             points = 0;
         }
         // also need to get transaction cost
         if (tryingToMove)
             transactionCost = houseToBuy.getTransactionCost();
         else
             transactionCost = 0;

         // get cash
         // if this is an inital purchase, we'll just skip the points
         if ((houseToBuy != null) && (houseToBuy.getInitializationAttempt())) {
             // simulating paying the downpayment
             initialDownPaymentBuffer = 0;
             boughtHome();
         } else if (myHH.raiseCash(downPayment + points + transactionCost)) {
             // we'll just make the downpayment disappear
             // we'll just make the transaction costs disappear
             myHH.payCash(downPayment + transactionCost);
             boughtHome();
             // send points to bank
             if (points > 0) {
                 myHH.payCash(points);
                 fundedMortgage.payPoints(points);
             }
         } else {
             myHH.raiseCashAnyway(downPayment + points);
             myHH.payCash(downPayment);
             boughtHome();
             // send points to bank
             if (points > 0) {
                 myHH.payCash(points);
                 fundedMortgage.payPoints(points);
             }
         }
         // we should now be ready to start making mortgage payments when its time
         // the household keeps track of this

         if (logData) {
            int priceInt = (int) homePrice;
            int downInt = (int) downPayment;
            int debtInt = (int) fundedMortgage.getDebtOwed();
            int paymentInt = (int) fundedMortgage.getMonthlyPayment();
            String intRate = String.format("%2.2f",(fundedMortgage.getTerms().getMonthlyInterestRate() * 1200));
            mortgageRecord.add(getDate() + " Bought home @ $" + priceInt + " Down pymt= $" + downInt +
                 " Borrowed $" + debtInt + " @ " + intRate + "% Payment= $" + paymentInt +
                 " from " + fundedMortgage.getBank().getName());
         }
     }

     // some clean-up
     private void boughtHome() {
         isHomeowner = true;
         if (!houseToBuy.getInitializationAttempt())
            movedInTheLastMonth = 30;
         ourHouse = houseToBuy;
         houseToBuy = null;
         houseMarket.homeBought();
         tryingToMove = false;
         tryingToRefinance = false;
         rent = 0;
         debug_preAppMortgageExpired = false;
     }

     void tryToPayMortgage(double payment) {
         // this should not interact with newMortgageState, becaue before we would pay on the new MOrtgage
         // it wil become the fundedMortgage, which is giverened by currentMortgageState
         if (myHH.raiseCash(payment)) {
             payMortgage(payment);
         } else {
             missedMortgagePayment++;
             fundedMortgage.missedPayment();
             missedPaymentLastMonth = 30;

             if (logData) {
                 if  (missedMortgagePayment < 3) {
                    mortgageRecord.add(getDate() + " Missed mortgage payment.");
                 } else if (missedMortgagePayment == 3) {
                    mortgageRecord.add(getDate() + " Missed 3 or more mortgage payments");
                 }
             }

         }
     }
          // allows fundedMortgage market to score utility of available offers
     public double calcMortgageUtility(MortgageTerms offer) {
        double utility = 0;
        double partUtil;

        // lengthInYears
        partUtil = ComputeMinMaxUtil(minLengthUtil, minLength,
                 maxLengthUtil, maxLength, offer.getLengthInYears()) / 30;
        if (partUtil == -1)    return -1;
        else                   utility += partUtil;

        // interestRate - HH look in a window if minimum to maximum
        partUtil = ComputeMinMaxUtil(minInterestRateUtil, minInterestRate,
                 maxInterestRateUtil, maxInterestRate, offer.getMonthlyInterestRate());
        if (partUtil == -1)    return -1;
        else                   utility += partUtil;

        // points
        partUtil = ComputeMinMaxUtil(minPointsUtil, minpoints,
                 maxPointsUtil, maxPoints, offer.getPoints());
        if (partUtil == -1)    return -1;
        else                   utility += partUtil;

        // isFixedRate
        // need to convert boolean into double for utility calc
        double isFixedRate = getDoubleFromBool(offer.getIsFixRate());
        partUtil = ComputeMinMaxUtil(minFixedRateUtil, minFixedRate,
                 minFixedRateUtil, maxFixedRate, isFixedRate);
        if (partUtil == -1)    return -1;
        else                   utility += partUtil;

        return utility;
     }

     private double ComputeMinMaxUtil(double minUtil, double minVal, double maxUtil, double maxVal, double offerVal) {
         double utility = 0;
         if ((offerVal < minVal) || (offerVal > maxVal))
             return -1;  // reject offer
         else
             utility = minUtil + (offerVal - minVal) * (maxUtil - minUtil)/(maxVal - minVal);
         return utility;
     }

     double getDoubleFromBool(Boolean b) {
         if (b) return 1;
         else   return 0;
     }

    public double getMonthlyHomeExpenses() {
        // maintain house
        // property tax (should depend on state)
        // insurance, maintenence
        // estimate at 5% of home price per year
        return ourHouse.getPurchasePrice() * 0.05 / 12;
    }

    public double payHomeExpenses() {
        double cashNeeded = 0;

        if (isHomeowner) {
            // maintain house
            // property tax (should depend on state)
            // insurance, maintenence
            // estimate at 5% of home price per year
            cashNeeded += getMonthlyHomeExpenses();
            if (rent != 0) {
                boolean wawaoo = true;
            }
        } else {
            // pay rent
            if ((monthsToNextRent == 0) || (rent == 0)) {
                double oldRent = rent;
                double amountAvailableForRent = myHH.getMonthlySalary() -  myHH.getMonthlyNonhousingExpenses();
                rent = houseMarket.getRent(myHH.getResidenceState(),  myHH.getIncomePctAverage());
                // can't pay more rent than we have
                if (rent > amountAvailableForRent)
                    rent = amountAvailableForRent * 0.95;

                // check if we can afford the rent
                double freeCash = myHH.getMyInfo().salary - cashNeeded;
                if (freeCash < 0) {
                    // no rent if the hh can't even afford living expenses
                    cashNeeded = freeCash;
                } else if (rent > freeCash) {
                    // lower the rent to a sustainable amount
                    rent = freeCash;
                }

                if (logData && (rent != oldRent)) {
                    int rentInt = (int) rent;
                    mortgageRecord.add(getDate() + " Rent = $" + rentInt);
                }

                monthsToNextRent = 12;
            }
            cashNeeded += rent;
        }

        return cashNeeded;
    }

    public void jobLoss() {
        if (logData) {
            mortgageRecord.add(getDate() + " Household lost a job");
        }
    }

    public void jobGain() {
        if (logData) {
            mortgageRecord.add(getDate() + " Household gained a job");
        }
    }

    public void reset() {
        tryingToMove = false;
        movedInTheLastMonth = 0;
        tryingToRefinance = false;
        refinancedInLastMonth = 0;
        missedPaymentLastMonth = 0;
        remodeling = false;
        preFundedMortgage = null;
        fundedMortgage = null;
        offerAccepted = false;
        debug_paidOffMortgage = false;
        debug_preAppMortgageExpired = false;
    }
}
